package com.semanticweb.webapp.controller;

import java.security.Principal;
import java.util.Collection;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.semanticweb.webapp.model.ontology.Item;
import com.semanticweb.webapp.ontology.Ont;
import com.semanticweb.webapp.service.OntologyService;
import com.semanticweb.webapp.service.RBACService;

/**
 * Home controller, site data is handled in this controller.
 * 
 * @author Srdjan Radulovic
 */
@Controller
@RequestMapping("/")
public class HomeController {

    private static final Logger LOGGER = LoggerFactory.getLogger(HomeController.class);

    @Autowired
    private OntologyService ontologyService;
    @Autowired
    private RBACService rbacService;

    @RequestMapping(method = RequestMethod.GET, value = "")
    public String index() {
        rbacService.createUsers();
        LOGGER.debug("Redirected to homepage");
        return "forward:/home";
    }

    @RequestMapping(method = RequestMethod.GET, value = "home")
    public @ResponseBody
    ModelAndView homePage(final Principal principal) {

        LOGGER.debug("Homepage is opened");

        final ModelAndView mav = new ModelAndView("home");
        authenticate(mav);

        return mav;
    }

    @RequestMapping(method = RequestMethod.GET, value = "search")
    public @ResponseBody
    ModelAndView searchPage() {

        LOGGER.debug("Basic search page is opened");

        final ModelAndView mav = new ModelAndView("search");
        authenticate(mav);

        return mav;
    }

    @RequestMapping(method = RequestMethod.POST, value = "search")
    public @ResponseBody
    ModelAndView searchRequest(@RequestParam("query") final String query) {

        LOGGER.error("Basic search request is requested with : " + query);

        final ModelAndView mav = new ModelAndView("search");
        authenticate(mav);
        final Collection<Item> result = ontologyService.findItems(query);
        mav.addObject("result", result);

        LOGGER.error("Basic search request returns : " + result);
        if (result != null && result.size() > 0) {
            for (final Item item : result) {
                LOGGER.error("ID - " + item.getInventaryNumber().getValue() + ", Subject - "
                        + item.getSubject().getValue());
            }
        }

        return mav;
    }

    @RequestMapping(method = RequestMethod.GET, value = "semantic")
    public @ResponseBody
    ModelAndView semanticPage() {

        LOGGER.debug("Semantic search page is opened");

        final ModelAndView mav = new ModelAndView("semantic");
        authenticate(mav);
        mav.addObject("prefixes", Ont.defaultQueryPrefixes());

        return mav;
    }

    @RequestMapping(method = RequestMethod.POST, value = "semantic")
    public @ResponseBody
    ModelAndView semanticRequest(@RequestParam("query") final String query) {

        LOGGER.debug("Semantic search is requested with query : " + query);

        final ModelAndView mav = new ModelAndView("semantic");
        authenticate(mav);
        final String result = ontologyService.findItemsBySparquelQuery(query);
        mav.addObject("result", result);
        mav.addObject("prefixes", Ont.defaultQueryPrefixes());

        return mav;
    }

    @RequestMapping(method = RequestMethod.GET, value = "catalogue")
    public @ResponseBody
    ModelAndView cataloguePage() {

        LOGGER.debug("Page for previewing list of items is opened");

        final ModelAndView mav = new ModelAndView("item-list");
        authenticate(mav);
        final Collection<Item> items = ontologyService.getAllItems();
        mav.addObject("items", items);

        return mav;
    }

    @RequestMapping(method = RequestMethod.GET, value = "preview/{id}")
    public @ResponseBody
    ModelAndView previewPage(@PathVariable("id") final String id) {

        LOGGER.debug("Page for previewing existing item is opened");

        final ModelAndView mav = new ModelAndView("item-edit");
        final String uname = authenticate(mav);

        final Item item = ontologyService.getItemByID(id);

        if (rbacService.canReadObject(id, uname)) {
            mav.addObject("item", item);
        } else {
            LOGGER.debug("User is not allowed to see requested file");
            mav.addObject("item", null); // do not allow user to see this item
        }

        mav.addObject("newItem", false);

        return mav;
    }

    @RequestMapping(method = RequestMethod.GET, value = "insert")
    public @ResponseBody
    ModelAndView insertPage() {

        LOGGER.debug("Page for creating new item is opened");

        final ModelAndView mav = new ModelAndView("item-edit");
        authenticate(mav);
        mav.addObject("item", new Item());
        mav.addObject("newItem", true);

        return mav;
    }

    @RequestMapping(method = RequestMethod.POST, value = "insert")
    public @ResponseBody
    ModelAndView savePage(@ModelAttribute("item") final Item item, final BindingResult result) {

        LOGGER.debug("Command for saving item is invoked");

        // if (result.hasErrors()) {
        // final ModelAndView mav = new ModelAndView("item-edit", "item", item);
        // authenticate(mav);
        // mav.addObject("newItem", true);
        // return mav;
        // }

        ontologyService.createItem(item);
        // TODO if doesn't work fetch item from ontology
        rbacService.addNewObject(item.getInventaryNumber().getValue());

        return new ModelAndView("redirect:/catalogue");
    }

    // @RequestMapping(method = RequestMethod.GET, value = "edit/{id}")
    // public @ResponseBody
    // ModelAndView editPage(@PathVariable("id") final String id) {
    //
    // LOGGER.debug("Page for previewing existing item is opened");
    //
    // final ModelAndView mav = new ModelAndView("item-edit");
    // final String uname = authenticate(mav);
    //
    // final Item item = ontologyService.getItemByID(id);
    //
    // if (rbacService.canUpdateObject(Long.parseLong(item.getId()), uname)) { // TODO check is id or inv num
    // mav.addObject("item", item);
    // mav.addObject("canSave", true);
    // mav.addObject("newItem", false);
    // } else {
    // LOGGER.debug("User is not allowed to update requested file");
    // mav.addObject("item", null); // do not allow user to see this item
    // }
    //
    // return mav;
    // }
    //
    // @RequestMapping(method = RequestMethod.POST, value = "edit/{id}")
    // public @ResponseBody
    // ModelAndView updatePage(@ModelAttribute("item") @Valid final Item item, final BindingResult result) {
    //
    // LOGGER.debug("Command for saving item is invoked");
    //
    // if (result.hasErrors()) {
    // final ModelAndView mav = new ModelAndView("edit/" + item.getId(), "item", item);
    // authenticate(mav);
    // mav.addObject("newItem", false);
    // mav.addObject("canSave", true);
    // return mav;
    // }
    //
    // ontologyService.createItem(item);// TODO
    // return new ModelAndView("redirect:/catalogue");
    // }

    @RequestMapping(method = RequestMethod.GET, value = "login")
    public @ResponseBody
    ModelAndView loginPage() {

        LOGGER.debug("Page for user login is opened");

        final ModelAndView mav = new ModelAndView("login");
        authenticate(mav);

        return mav;
    }

    @InitBinder
    public void initBinder(final WebDataBinder binder) {
        binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
    }

    private String authenticate(final ModelAndView mav) {
        final Authentication a = SecurityContextHolder.getContext().getAuthentication();
        String username = null;
        if (a.getPrincipal() instanceof User) {
            final User currentUser = (User) a.getPrincipal();
            username = currentUser.getUsername();
            mav.addObject("user", currentUser.isEnabled());
        }
        return username;
    }

}
